use anyhow::anyhow;
use chrono::{Datelike, NaiveDate};
use clickhouse_rs::types::{DateTimeType, Decimal, SqlType};

use crate::base::{
    get_date_1970, get_date_time_1900, get_date_time_1970, parse_date, parse_date_time,
};
use super::column_datas::DbValue;
pub (super) fn to_db_value<'a>(val: &'a str, st: &SqlType) -> anyhow::Result<DbValue<'a>> {
    match st {
        SqlType::Bool => {
            let v = if val.is_empty() { false } else { val.parse()? };
            Ok(DbValue::Bool(v))
        }
        SqlType::UInt8 => {
            let v = if val.is_empty() { 0u8 } else { val.parse()? };
            Ok(DbValue::UInt8(v))
        }
        SqlType::UInt16 => {
            let v = if val.is_empty() { 0u16 } else { val.parse()? };
            Ok(DbValue::UInt16(v))
        }
        SqlType::UInt32 => {
            let v = if val.is_empty() { 0u32 } else { val.parse()? };
            Ok(DbValue::UInt32(v))
        }
        SqlType::UInt64 => {
            let v = if val.is_empty() { 0u64 } else { val.parse()? };
            Ok(DbValue::UInt64(v))
        }
        SqlType::Int8 => {
            let v = if val.is_empty() { 0i8 } else { val.parse()? };
            Ok(DbValue::Int8(v))
        }
        SqlType::Int16 => {
            let v = if val.is_empty() { 0i16 } else { val.parse()? };
            Ok(DbValue::Int16(v))
        }
        SqlType::Int32 => {
            let v = if val.is_empty() { 0i32 } else { val.parse()? };
            Ok(DbValue::Int32(v))
        }
        SqlType::Int64 => {
            let v = if val.is_empty() { 0i64 } else { val.parse()? };
            Ok(DbValue::Int64(v))
        }
        SqlType::String => Ok(DbValue::Str(val)),
        SqlType::FixedString(w) => {
            if val.len() > *w as usize {
                return Err(anyhow!("值{}的长度超过了FixedString({})", val, w));
            }
            Ok(DbValue::Str(val))
        }
        SqlType::Float32 => {
            let v = if val.is_empty() { 0.0f32 } else { val.parse()? };
            Ok(DbValue::Float32(v))
        }
        SqlType::Float64 => {
            let v = if val.is_empty() { 0.0f64 } else { val.parse()? };
            Ok(DbValue::Float64(v))
        }
        SqlType::Decimal(p, s) => {
            let val = val.trim().trim_end_matches('0');
            if val.is_empty() {
                return Ok(DbValue::Decimal(Decimal::of(0, *s)));
            }
            let mut is_find = false;
            let p2 = 0u8;
            let s2 = 0u8;
            let mut bvals: Vec<u8> = Vec::new();
            for (i, c) in val.as_bytes().iter().enumerate() {
                if *c == b'-' {
                    if i != 0 {
                        return Err(anyhow!("值{}不是有效的Decimal(p={},s={})", val, p, s));
                    }
                    bvals.push(*c);
                } else if *c == b'.' {
                    if i == 0 || is_find {
                        return Err(anyhow!("值{}不是有效的Decimal(p={},s={})", val, p, s));
                    }
                    is_find = true;
                } else if !c.is_ascii_digit() {
                    return Err(anyhow!("值{}不是有效的Decimal(p={},s={})", val, p, s));
                } else {
                    bvals.push(*c);
                }
            }
            if p2 > *p || s2 > *s {
                return Err(anyhow!("值{}的精度超过了Decimal(p={},s={})", val, p, s));
            }
            let nval = unsafe { String::from_utf8_unchecked(bvals) };
            let iv: i64 = nval.parse()?;
            let dv = Decimal::new(iv, *s);
            Ok(DbValue::Decimal(dv))
        }
        SqlType::Date => {
            let v: NaiveDate = if val.is_empty() || val == "0" {
                get_date_1970()
            } else {
                let mut nv = parse_date(val.as_ref())?;
                if nv.year() < 1970 {
                    nv = get_date_1970();
                }
                nv
            };
            Ok(DbValue::Date(v))
        }
        SqlType::DateTime(dtt) => {
            let (ndt, tz) = if val.is_empty() || val == "0" {
                match dtt {
                    DateTimeType::DateTime32 => (get_date_time_1970(), chrono_tz::UTC),
                    DateTimeType::DateTime64(_, tz) => (get_date_time_1900(), *tz),
                    DateTimeType::Chrono => (get_date_time_1900(), chrono_tz::UTC),
                }
            } else {
                let ndt = parse_date_time(val.as_ref())?;
                if let DateTimeType::DateTime64(_, tz) = dtt {
                    (ndt, *tz)
                } else {
                    (ndt, chrono_tz::UTC)
                }
            };
            match dtt {
                DateTimeType::DateTime32 => {
                    if ndt.year() < 1970 {
                        anyhow::bail!("对于DateTime32值{}的年份小于1970年", val)
                    }
                }
                _ => {
                    if ndt.year() < 1900 {
                        anyhow::bail!("对于DateTime64值{}的年份小于1900年", val)
                    }
                }
            }
            let dtz = ndt
                .and_local_timezone(tz)
                .single()
                .ok_or_else(|| anyhow::anyhow!("无法将日期时间转换为指定时区"))?;
            Ok(DbValue::DateTime(dtz))
        }
        SqlType::LowCardinality(t) => {
            let v = to_db_value(val, t)?;
            Ok(v)
        }
        _ => Err(anyhow!("etl程序目前不支持数据类型: {}", st)),
    }
}
